#!/usr/bin/env node
import {EOL} from 'os';
import {readFileSync, writeFileSync} from 'fs';
import {dirname, basename, join} from 'path';
import {Command} from 'commander';
import gradient from 'gradient-string';
import {getUser, initDeezerApi, searchMusic} from 'd-fi-core';
import prompts from 'prompts';
import logUpdate from 'log-update';
import pMap from 'p-map';
import signale from './lib/signale';
import downloadTrack from './lib/download-track';
import parseInfo from './lib/parse-info';
import conf from './lib/config';
import updateCheck from './lib/update-check';
import pkg from '../package.json';
import type {trackType} from 'd-fi-core/dist/types';

// Check for update
updateCheck(pkg);

// App info
console.log(
  gradient('red', 'yellow', 'orange')(' ──────────────────────────────────────────────') +
    '\n' +
    gradient('red', 'yellow', 'orange')(`             ♥ d-fi - ${pkg.version} ♥         `) +
    '\n' +
    gradient('orange', 'yellow', 'red')(' ──────────────────────────────────────────────') +
    '\n' +
    gradient('red', 'yellow', 'orange')('│ REPO   --> https://notabug.org/sayem314/d-fi │') +
    '\n' +
    gradient('red', 'yellow', 'orange')('│ DONATE --> https://sayem.eu.org/donate       │') +
    '\n' +
    gradient('red', 'yellow', 'orange')(' ──────────────────────────────────────────────'),
);

const cmd = new Command()
  .option('-q, --quality <quality>', 'The quality of the files to download: 128/320/flac ')
  .option('-o, --output <template>', 'Output filename template')
  .option('-u, --url <url>', 'Deezer album/artist/playlist/track url')
  .option('-i, --input-file <file>', 'Downloads all urls listed in text file')
  .option(
    '-c, --concurrency <number>',
    'Download concurrency for album, artists and playlist',
    conf.get('concurrency', '4') as string,
  )
  .option('-a, --set-arl <string>', 'Set arl cookie')
  .option('-d, --headless', 'Run in headless mode for scripting automation', false);

const options = cmd.parse(process.argv).opts();
if (!options.url && cmd.args[0]) {
  options.url = cmd.args[0];
}
if (options.headless && !options.quality) {
  console.error(signale.error('Missing parameters --quality'));
  console.error(signale.note('Quality must be provided with headless mode'));
  process.exit(1);
}
if (options.headless && !options.url && !options.inputFile) {
  console.error(signale.error('Missing parameters --url'));
  console.error(signale.note('URL must be provided with headless mode'));
  process.exit(1);
}

const onCancel = () => {
  console.info(signale.note('Aborted!'));
  process.exit();
};

const startDownload = async (saveLayout: any, url: string, skipPrompt: boolean) => {
  if (!options.quality) {
    const {musicQuality} = await prompts(
      [
        {
          type: 'select',
          name: 'musicQuality',
          message: 'Select music quality:',
          choices: [
            {title: 'MP3  - 128 kbps', value: '128'},
            {title: 'MP3  - 320 kbps', value: '320'},
            {title: 'FLAC - 1411 kbps', value: 'flac'},
          ],
          initial: 1,
        },
      ],
      {onCancel},
    );
    options.quality = musicQuality;
  }

  if (!url) {
    const {query} = await prompts(
      [
        {
          type: 'text',
          name: 'query',
          message: 'Enter URL or search:',
          validate: (value) => (value ? true : false),
        },
      ],
      {onCancel},
    );
    url = query;
  }

  try {
    let data = await parseInfo(url);
    if (!data) {
      if (options.headless) {
        throw new Error('Please provide a valid URL. Unknown URL: ' + url);
      }

      const {TRACK} = await searchMusic(url, ['TRACK']);
      data = {
        info: {type: 'track', id: url},
        linktype: 'track',
        linkinfo: {},
        tracks: TRACK.data,
      };
    }

    if (!options.headless && data.tracks.length > 1) {
      const choices: {items: trackType[]} = await prompts(
        [
          {
            type: 'multiselect',
            name: 'items',
            message: `Select songs to download. Total of ${data.tracks.length} tracks.`,
            choices: data.tracks.map((t) => ({
              title: t.SNG_TITLE,
              value: t,
              description: `by ${t.ART_NAME}`,
            })),
          },
        ],
        {onCancel},
      );

      data.tracks = choices.items;
    }

    if (data && data.tracks.length > 0) {
      console.log(signale.info(`Proceeding download of ${data.tracks.length} tracks. Be patient.`));
      let trackNumber = conf.get('trackNumber', true) as boolean;
      let m3u8: string[] = [];
      let savedFiles: string[] = [];

      await pMap(
        data.tracks,
        async (track, index) => {
          const savedPath = await downloadTrack({
            track,
            quality: options.quality,
            info: (data as any).linkinfo,
            path: options.output ? options.output : saveLayout[(data as any).linktype],
            totalTracks: data ? data.tracks.length : 10,
            trackNumber,
            message: `(${index}/${(data as any).tracks.length})`,
          });
          if (savedPath) {
            m3u8.push(basename(savedPath));
            savedFiles.push(savedPath);
          }
        },
        {concurrency: Number(options.concurrency)},
      );
      if (m3u8.length > 0) {
        const playlistDir = dirname(savedFiles[0]);
        const playlistFile = join(playlistDir, basename(playlistDir) + '.m3u8');
        const m3u8Content = '#EXTM3U' + EOL + m3u8.sort().join(EOL);
        writeFileSync(playlistFile, m3u8Content, {encoding: 'utf-8'});
      }
    } else {
      console.log(signale.info('No items to download!'));
    }
  } catch (err) {
    console.error(signale.error(err.message));
  }

  // Ask for new download
  if (!options.headless && !skipPrompt) {
    startDownload(saveLayout, '', skipPrompt);
  }
};

/**
 * Application init.
 */
const initApp = async () => {
  if (options.setArl) {
    const configPath = conf.set('cookies.arl', options.setArl);
    console.log(signale.info('cookies.arl set to --> ' + options.setArl));
    console.log(signale.note(configPath));
    process.exit();
  }

  logUpdate(signale.pending('Initializing session...'));
  const arl = conf.get('cookies.arl') as string;
  logUpdate(signale.pending('Verifying session...'));
  await initDeezerApi(arl);
  const {BLOG_NAME} = await getUser();
  logUpdate(signale.success('Logged in as ' + BLOG_NAME));
  logUpdate.done();

  const saveLayout: any = conf.get('saveLayout');
  if (options.inputFile) {
    const lines = readFileSync(options.inputFile, 'utf-8').split(/\r?\n/);
    for await (const line of lines) {
      if (line && line.match(/https?:\/\/.*\w+\.\w+\/\w+/)) {
        console.log(signale.info('Starting download: ' + line));
        await startDownload(saveLayout, line.trim(), true);
      }
    }
  } else {
    startDownload(saveLayout, options.url, false);
  }
};

initApp().catch((err) => {
  console.error(signale.error(err.message));
  process.exit(1);
});
